/*
客户端组件，用于模拟游戏客户端连接和消息处理

	管理与游戏服务器的TCP连接
	支持消息发送和接收
	自动处理连接状态
	实现了Component接口，可与容器集成
	包含简单的消息收发测试逻辑

2025/10/07
增加编码解码，消息协议后修改
*/
package client

import (
	// 网络消息读取常需按 “行” 解析（如以\n结尾），bufio.Reader提供高效的行读取能力
	"fmt"
	"gameServer/encoding/json"
	"gameServer/log"
	"gameServer/packet"
	"net"
)

// Client 客户端组件
type Client struct {
	serverAddr string      // ip:port
	conn       net.Conn    // 网络连接实例 一个通用的网络连接，是一个接口，实现tcp或者udp都用这个进行连接
	connected  bool        // 连接状态标记 true 链接
	codec      *json.Codec //使用JSON编码、解码器
}

// 创建一个客户端组件
func NewClient(serverAddr string) *Client {
	return &Client{
		serverAddr: serverAddr,
		codec:      &json.Codec{},
		// 其他参数，默认nil,false
	}
}

// 连接到服务器
func (c *Client) Connect() error {
	// 调用net.Dial建立TCP连接：参数1=网络类型（"tcp"），参数2=服务器地址（c.serverAddr）
	conn, err := net.Dial("tcp", c.serverAddr)
	if err != nil {
		return fmt.Errorf("failed to connect to server: %s for %v", c.serverAddr, err)
	}
	c.conn = conn
	c.connected = true
	log.Infof("Connected to server at %s", c.serverAddr)
	return nil
}

// 断开连接
func (c *Client) Disconnect() error {
	// 基本上都需要判断是否已经有链接
	if c.conn != nil {
		err := c.conn.Close()
		c.connected = false
		log.Infof("Disconnected from server")
		return err
	}
	return nil
}

// 发送消息到服务器
func (c *Client) SendMessage(route uint32, message interface{}) error {
	// 这里为什么用uint32???直接用16不就好了，这里可能是为了用uint32计算方便
	if !c.connected {
		return fmt.Errorf("not connected to server")
	}
	// 使用编码器编码信息 （这里就做了一个0消息，用于测试）
	data, err := c.codec.Encode(uint16(route), 0, message)
	if err != nil {
		return fmt.Errorf("failed to encode message: %v", err)
	}
	_, err = c.conn.Write(data)
	// // 调用fmt.Fprintf向conn格式化写入消息：参数1=写入目标（conn实现了io.Writer接口），参数2=消息格式
	// // 消息末尾加""：约定服务器按“行”解析消息（与StartReceiving的ReadString('')对应）
	// _, err := fmt.Fprintf(c.conn, message+"")
	if err != nil {
		c.connected = false
		return fmt.Errorf("failed to send message for %v", err)
	}
	log.Infof("Send message: %s ", message)
	return nil
}

// 开始接收服务器消息
func (c *Client) StartReceiving() {
	if !c.connected {
		return
	}
	// 单独协程去接收
	// 启动匿名goroutine：异步接收消息（避免阻塞主协程）
	go func() {
		// 增加编码后的接收消息
		buffer := make([]byte, 4096)
		// // 创建bufio.Reader：包装conn，提供带缓冲的行读取能力（比直接用conn.Read高效）
		// reader := bufio.NewReader(c.conn)
		for c.connected {
			n, err := c.conn.Read(buffer)
			if err != nil {
				log.Errorf("Failed to read message: %v", err)
				c.conn.Close()
				return
			}
			// 解包数据
			pkt, err := packet.Unpack(buffer[:n])
			if err != nil {
				log.Errorf("Failed to unpack message: %v", err)
				continue
			}
			switch pkt.Header {
			case packet.FlagData:
				log.Infof("Received message with route %d: %s", pkt.Route, string(pkt.Body))
			case packet.FlagHeartbeat:
				log.Infof("Received heartbeat")
			default:
				log.Infof("Received Unidentifiable data packets %v ", pkt.Header)
				c.connected = false
				return
			}

			// // 独特修改，设置读取超时，避免永久阻塞，一旦超时，这里ReadString会返回err
			// // c.conn.SetReadDeadline(time.Now().Add(1 * time.Second))

			// // 调用ReadString('')：读取到第一个''为止，返回包含''的完整消息
			// message, err := reader.ReadString('')
			// // 读取失败直接断开连接？？？
			// // 错误处理：读取失败（如连接断开、服务器关闭）
			// if err != nil {
			// 	// // 检查是否是超时错误，如果是则继续循环
			// 	// if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
			// 	// 	continue
			// 	// }
			// 	log.Infof("Failed to read message: %v End connection ", err)
			// 	c.connected = false
			// 	return
			// }
			// // 输出的时候去除换行符
			// log.Infof("Receive from server: %s", message[:len(message)-1])
		}
	}()
}

// 检查是否已连接
func (c *Client) IsConnected() bool {
	return c.connected
}

// 初始化组件
func (c *Client) Init() error {
	log.Info("Client component is initializing...")
	return nil
}

// 启动组件
func (c *Client) Start() error {
	log.Info("Client component is starting...")
	// 链接到服务器
	if err := c.Connect(); err != nil {
		return err
	}
	// 开始接收消息
	c.StartReceiving()

	// 发起测试消息
	// go func() {
	// 	// 延迟100ms：等待连接和接收逻辑初始化完成（避免消息发送过早失败）
	// 	time.Sleep(100 * time.Millisecond)
	// 	// 循环发送5条测试消息（模拟玩家操作）
	// 	for i := 0; i < 5; i++ {
	// 		if c.IsConnected() {
	// 			// 格式化测试消息：包含消息序号（i+1）
	// 			c.SendMessage(fmt.Sprintf("Hello from client! Message #%d", i+1))
	// 			// 间隔1秒发送一条（模拟真实场景的消息间隔）
	// 			time.Sleep(1 * time.Second)
	// 		}
	// 	}
	// }()
	return nil
}

// 停止组件
func (c *Client) Stop() error {
	log.Info("Client component is stopping...")
	return c.Disconnect()
}

/*这里有一种手法，就是客户端模拟，不是单纯为了能使用Component而使用，是写好基础服务以后加入到接口中*/
/*Start函数里面更是，在这里写具体测试实例，不影响客户端本身链接功能*/
